from datetime import datetime, timedelta, UTC
from typing import Optional, Dict, Any
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from app.database.models import User
from app.core.config import settings
import logging

# 配置日志记录器
logger = logging.getLogger(__name__)

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证密码"""
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    """生成密码哈希"""
    return pwd_context.hash(password)

def create_token(data: dict, expires_delta: Optional[timedelta] = None, is_refresh_token: bool = False) -> str:
    """创建JWT令牌
    
    Args:
        data: 要编码的数据
        expires_delta: 过期时间增量
        is_refresh_token: 是否是刷新令牌
        
    Returns:
        str: JWT令牌
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(UTC) + expires_delta
    else:
        # 如果是刷新令牌，使用刷新令牌的过期时间
        minutes = (settings.JWT_REFRESH_TOKEN_EXPIRE_MINUTES 
                  if is_refresh_token 
                  else settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
        expire = datetime.now(UTC) + timedelta(minutes=minutes)
    
    to_encode.update({
        "exp": expire,
        "type": "refresh" if is_refresh_token else "access"
    })
    
    encoded_jwt = jwt.encode(
        to_encode, 
        settings.JWT_SECRET_KEY, 
        algorithm=settings.JWT_ALGORITHM
    )
    return encoded_jwt

def create_tokens(user_data: dict) -> Dict[str, str]:
    """创建访问令牌和刷新令牌
    
    Args:
        user_data: 用户数据
        
    Returns:
        Dict[str, str]: 包含访问令牌和刷新令牌的字典
    """
    access_token = create_token(user_data)
    refresh_token = create_token(user_data, is_refresh_token=True)
    
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": settings.JWT_TOKEN_TYPE
    }

def verify_token(token: str, is_refresh_token: bool = False) -> Dict[str, Any]:
    """验证JWT令牌
    
    Args:
        token: JWT令牌
        is_refresh_token: 是否是刷新令牌
        
    Returns:
        Dict[str, Any]: 解码后的令牌数据
        
    Raises:
        HTTPException: 令牌无效或过期
    """
    try:
        payload = jwt.decode(
            token, 
            settings.JWT_SECRET_KEY, 
            algorithms=[settings.JWT_ALGORITHM]
        )
        
        # 验证令牌类型
        token_type = payload.get("type")
        expected_type = "refresh" if is_refresh_token else "access"
        if token_type != expected_type:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=f"无效的令牌类型，需要{expected_type}令牌",
                headers={"WWW-Authenticate": settings.JWT_TOKEN_PREFIX},
            )
            
        return payload
        
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的认证凭据或令牌已过期",
            headers={"WWW-Authenticate": settings.JWT_TOKEN_PREFIX},
        )

def create_user(db: Session, username: str, email: str, password: str) -> User:
    """创建新用户"""
    # 检查用户名是否已存在
    if db.query(User).filter(User.username == username).first():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="用户名已被注册"
        )
    
    # 检查邮箱是否已存在
    if db.query(User).filter(User.email == email).first():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="邮箱已被注册"
        )
    
    # 创建新用户
    hashed_password = get_password_hash(password)
    db_user = User(
        username=username,
        email=email,
        hashed_password=hashed_password
    )
    
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

def authenticate_user(db: Session, username: str, password: str) -> Optional[User]:
    """验证用户并返回用户对象
    
    Args:
        db: 数据库会话
        username: 用户名
        password: 密码
        
    Returns:
        Optional[User]: 验证成功返回用户对象，验证失败返回 None
    """
    try:
        # 查找用户
        user = db.query(User).filter(User.username == username).first()
        if not user:
            return None
            
        # 验证密码
        if not verify_password(password, user.hashed_password):
            return None
            
        return user
        
    except Exception as e:
        logger.error(f"用户验证失败: {str(e)}", exc_info=True)
        return None

def get_current_user(db: Session, token: str) -> User:
    """获取当前用户
    
    Args:
        db: 数据库会话
        token: JWT令牌
        
    Returns:
        User: 用户对象
    """
    try:
        # 移除Bearer前缀（如果存在）
        if token.startswith(settings.JWT_TOKEN_PREFIX + " "):
            token = token[len(settings.JWT_TOKEN_PREFIX + " "):]
        
        # 验证令牌
        payload = verify_token(token)
        username = payload.get("sub")
        
        if not username:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="无效的认证凭据",
                headers={"WWW-Authenticate": settings.JWT_TOKEN_PREFIX},
            )
        
        # 获取用户
        user = db.query(User).filter(User.username == username).first()
        if user is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户不存在",
                headers={"WWW-Authenticate": settings.JWT_TOKEN_PREFIX},
            )
        return user
        
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的认证凭据或令牌已过期",
            headers={"WWW-Authenticate": settings.JWT_TOKEN_PREFIX},
        ) 